//-------------------------------------------------------------------
//	Copyright (c) 2010-2012 Zhirnov Andrey
//	This file is part of the "UXGEN" project.
//	See copyright notice in "Engine.h".
//-------------------------------------------------------------------

#include "ConfigTypes.h"

namespace Engine
{
namespace ConfigTypes
{
	
// ----------------------- CConfigBaseType ----------------------- //

	CConfigBaseType::CConfigBaseType(config_node_t pParent, e_config_object::type eType):
		_pNode(), _sName(), _iRefCount(1), _eType(eType)
	{
		_pNode = pParent.PushBack( this );
	}

	CConfigBaseType::CConfigBaseType(config_node_t pParent, const char *pName, e_config_object::type eType):
		_pNode(), _sName(pName), _iRefCount(1), _eType(eType)
	{
		_pNode = pParent.PushBack( this );
	}

	CConfigBaseType::~CConfigBaseType()
	{
		_pNode.Elem() = nullptr;
	}

	bool CConfigBaseType::Free()
	{
		if ( --_iRefCount == 0 ) {
			delete this;
			return true;
		}
		return false;
	}
	
	bool CConfigBaseType::SetName(const char *pName)
	{
		if ( pName == nullptr || !_sName.Empty() )
			return false;

		_sName = pName;
		return true;
	}
	
	bool CConfigBaseType::Copy(config_const_node_t pFields)
	{
		s_RecursiveCopyFields( _pNode, pFields );
		return true;
	}
	
	bool CConfigBaseType::CopyObj(config_const_node_t pFields)
	{
		for (uint i = 0; i < pFields.Count(); ++i)
			s_RecursiveCopyFields( _pNode, pFields[i] );

		return true;
	}


	void CConfigBaseType::s_RecursiveCopyFields(config_node_t pNode, config_const_node_t pFields)
	{
		if ( !pFields.Valid() )
			return;

		pNode = pNode.PushBack( pFields.Elem() );
		pFields.Elem()->AddRef();

		for (uint i = 0; i < pFields.Count(); ++i)
		{
			s_RecursiveCopyFields( pNode, pFields[i] );
		}
	}


// ----------------------- CConfigValueType ---------------------- //
	
	CConfigValueType::CConfigValueType(config_node_t pParent, const char *pName):
		CConfigBaseType(pParent, pName, e_config_object::VALUE), _i64Value(0), _eValType(e_value::UNKNOWN)
	{
	}
		
	CConfigValueType::CConfigValueType(const CConfigValueType &cObj, config_node_t pParent, const char *pName):
		CConfigBaseType(pParent, pName, e_config_object::VALUE), _i64Value(cObj._i64Value), _eValType(cObj._eValType)
	{
	}

	CConfigValueType::~CConfigValueType()
	{
	}
	
	void CConfigValueType::GetValue(string &str) const
	{
		switch( _eValType )
		{
			case e_value::BOOL	 :	str << _bValue;		break;
			case e_value::INT8	 :
			case e_value::INT16	 :
			case e_value::INT32	 :	str << _iValue;		break;
			case e_value::INT64	 :	str << _i64Value;	break;
			case e_value::FLOAT	 :	str << _fValue;		break;
			case e_value::DOUBLE :	str << _dValue;		break;
			default				 :	return;
		}
	}
	
	bool CConfigValueType::SetBValue(bool bValue)
	{
		if ( _eValType != e_value::UNKNOWN )
			return false;

		_bValue	  = bValue;
		_eValType = e_value::BOOL;
		return true;
	}
	
	bool CConfigValueType::CopyValue(CConfigBaseType *pObj, bool bInc)
	{
		if ( pObj == nullptr || pObj->GetType() != GetType() )
			return false;

		CConfigValueType	*p_obj = (CConfigValueType*)pObj;

		_eValType	= p_obj->_eValType;
		memcpy( _aBuf, p_obj->_aBuf, sizeof(_aBuf) );

		if ( bInc )
		{
			switch ( _eValType )
			{
				case e_value::INT8	:
				case e_value::INT16	:
				case e_value::INT32 :	++_iValue;		break;
				case e_value::INT64	:	++_i64Value;	break;
				case e_value::FLOAT	:	++_fValue;		break;
				case e_value::DOUBLE:	++_dValue;		break;
				default				:	WARNING( "can't inc not integer or float value" );
			}
		}

		return true;
	}
	
	void CConfigValueType::SetDefault()
	{
		_eValType = e_value::INT32;
		_iValue = 0;
	}

	bool CConfigValueType::SetValue(const char *pValue, uint uLength, uint *pErrorPos)
	{
		if ( _eValType != e_value::UNKNOWN )
		{
			if ( pErrorPos != nullptr ) pErrorPos = 0;
			return false;
		}

		if ( (pValue[0] >= '0' && pValue[0] <= '9') || pValue[0] == '-' )
		{
			bool	sign	= (pValue[0] == '-');
			uint	i		= sign ? 1 : 0,
					mult	= 10;
			uint64	val		= 0;


			if ( uLength > 1 && pValue[0] == '0' &&
				( pValue[1] == 'x' || pValue[1] == 'X' ) )
			{
			// Int 16bit
				mult = 16;
				i = 2;

				for (; i < uLength; ++i)
				{
					if ( pValue[i] >= '0' && pValue[i] <= '9' )		(val *= mult) += pValue[i] - '0';		else
					if ( pValue[i] >= 'a' && pValue[i] <= 'f' )		(val *= mult) += pValue[i] - 'a'+10;		else
					if ( pValue[i] >= 'A' && pValue[i] <= 'F' )		(val *= mult) += pValue[i] - 'A'+10;
					else	goto Label1;
				}
			}
			else
			// Int 10bit
			{
				for (; i < uLength; ++i)
				{
					if ( pValue[i] >= '0' && pValue[i] <= '9' )		(val *= mult) += pValue[i] - '0';
					else	goto Label1;
				}
			}

			if ( sign && val <= MaxValue<int32>() )
			{
				_iValue		= SetSign( (int32)val, sign );
				_eValType	= e_value::INT32;
			}
			else	
			if ( !sign && val <= MaxValue<uint32>() )
			{
				_iValue		= (uint32)val;
				_eValType	= e_value::INT32;
			}
			else
			{
				_eValType	= e_value::INT64;
				_i64Value	= SetSign( (int64)val, sign );
			}
			return true;
			
Label1:
			if ( pValue[i] == '.' && mult == 10 )
			{
				uint32	div		= 0;
				int		dcount	= 0;
				int		ipow	= 0;
				bool	signpow	= false;

				++i;

				// Float
				for (;  i < uLength; ++i)
				{
					if ( pValue[i] >= '0' && pValue[i] <= '9' )
					{
						(div *= 10) += pValue[i] - '0';
						++dcount;
					}
					else
					if ( pValue[i] == 'e' || pValue[i] == 'E' )	{ ++i;	break; }
					else	goto Label3;
				}

				if ( pValue[i] == '-' )	{ ++i;	signpow = true; }	else
				if ( pValue[i] == '+' )	{ ++i;	signpow = false; }

				for (; i < uLength; ++i)
				{
					if ( pValue[i] >= '0' && pValue[i] <= '9' )		(ipow *= 10) += pValue[i] - '0';	
					else	goto Label3;
				}

				ipow		= SetSign( ipow, signpow );
				double	dv	= SetSign( (double)val, sign ) + div * Pow( 10.f, ipow - dcount );

				if ( dv > MinValue<float>() && dv < MaxValue<float>() ) {
					TCheckConverter::Convert( _fValue, dv );
					_eValType = e_value::FLOAT;
				}
				else {
					_dValue	  = dv;
					_eValType = e_value::DOUBLE;
				}
				return true;
			}

Label3:
			if ( pErrorPos != nullptr )	*pErrorPos = i;
			return false;
		}
		else
		if ( _stricmp( pValue, "true" ) == 0 )
		{
			_bValue   = true;
			_eValType = e_value::BOOL;
		}
		else
		if ( _stricmp( pValue, "false" ) == 0 )
		{
			_bValue   = false;
			_eValType = e_value::BOOL;
		}
		else
		{
			if ( pErrorPos != nullptr )	*pErrorPos = 0;
			return false;
		}

		return true;
	}


// ------------------------ CConfigObjectType -------------------- //
	
	CConfigObjectType::CConfigObjectType(config_node_t pParent):
		CConfigBaseType(pParent, e_config_object::OBJECT), _sParams(), _bInitList(false)
	{
	}

	CConfigObjectType::~CConfigObjectType()
	{
	}

	bool CConfigObjectType::SetParams(const char *pParams)
	{
		if ( !_sParams.Empty() )
			return false;

		_sParams = pParams;
		return true;
	}

	
// ------------------------ CConfigStringType -------------------- //
	
	CConfigStringType::CConfigStringType(config_node_t pParent, const char *pName):
		CConfigBaseType(pParent, pName, e_config_object::STRING), _sValue()
	{
	}

	CConfigStringType::CConfigStringType(const CConfigStringType &cObj, config_node_t pParent, const char *pName):
		CConfigBaseType(pParent, pName, e_config_object::STRING), _sValue(cObj._sValue)
	{
	}

	CConfigStringType::~CConfigStringType()
	{
	}

	inline uint8 CharToCode(char c)
	{
		if ( c >= '0' && c <= '9' )	return c - '0';
		if ( c >= 'a' && c <= 'f' ) return c - 'a' + uint8(10);
		if ( c >= 'A' && c <= 'F' )	return c - 'A' + uint8(10);
		
		WARNING( "incorrect char" );
		return 0;
	}
		
	bool CConfigStringType::SetValue(const char *pValue, uint uLength)
	{
		if ( !_sValue.Empty() )
			return false;

		for (uint i = 0; i < uLength; ++i)
		{
			if ( *pValue == '\\' )
			{
				++pValue;

				switch ( *pValue )
				{
					case '\\' :
						_sValue << '\\';
						break;

					case '"' :
						_sValue << '"';
						break;

					case 'n' :
						_sValue << NEWLINE;
						break;

					case 'x' :
						_sValue << CharToCode( *pValue ) << 1 + CharToCode( *(++pValue) );
						break;

					case '0' :
						_sValue << '\0';

					default :
						WARNING( "unknown symbol" );
				};
			}
			else
				_sValue << *pValue;
			
			++pValue;
		}
		
		return true;
	}


	
	bool CreateType(CConfigBaseType *pObj, config_node_t pParent, const char *pName, CConfigBaseType *&pNewObj)
	{
		if ( pObj == nullptr )
			return false;

		switch ( pObj->GetType() )
		{
			case e_config_object::VALUE	 :
				pNewObj = new CConfigValueType( *(CConfigValueType*)pObj, pParent, pName );
				break;

			case e_config_object::STRING :
				pNewObj = new CConfigStringType( *(CConfigStringType*)pObj, pParent, pName );
				break;

			case e_config_object::OBJECT :
			case e_config_object::ARRAY	 :
			case e_config_object::STRUCT :
				pNewObj = new CConfigBaseType( pParent, pName, e_config_object::FIELD );
				pNewObj->Copy( pObj->GetNode() );
				break;

			default	:
				return false;
		}

		return true;
	}

//-------------------------------------------------------------------

}	// ConfigTypes
}	// Engine